---
sidebar_position: 3
---

# Создание статической библиотеки

## Что такое библиотека и какая она бывает

:::info

Материал для подготовки взят с [A.1 – Статические и динамические библиотеки](https://radioprog.ru/post/1084).

:::

### Библиотека

**Библиотека** – это пакет кода, который предназначен для повторного использования многими программами. Обычно библиотека C++ состоит из двух частей:

1. заголовочный файл, который определяет функциональность, которую библиотека предоставляет (предлагает) программам, использующим ее;
2. предварительно скомпилированный двоичный файл, который содержит реализацию этой функциональности, предварительно скомпилированную в машинный код.

Некоторые библиотеки могут быть разделены на несколько файлов и/или иметь несколько файлов заголовков.

Библиотеки предварительно скомпилированы по нескольким причинам:

1. Во-первых, поскольку библиотеки меняются редко, их не нужно часто перекомпилировать. Было бы пустой тратой времени перекомпилировать библиотеку каждый раз, когда вы пишете программу, которая ее использует.
2. Во-вторых, поскольку предварительно скомпилированные объекты представлены машинным кодом, люди не могут получить доступ к исходному коду или изменить его, что важно для предприятий или людей, которые не хотят делать свой исходный код доступным из соображений интеллектуальной собственности.

Существует два типа библиотек: **статические библиотеки** и **динамические библиотеки**.

### Статическая библиотека

**Статическая библиотека** (иногда называемая *archive*, "архив") состоит из подпрограмм, которые скомпилированы и линкуются непосредственно с вашей программой. Когда вы компилируете программу, использующую статическую библиотеку, все функции статической библиотеки, которые использует ваша программа, становятся частью вашего исполняемого файла. В Windows статические библиотеки обычно имеют расширение `.lib`, а в Linux – расширение `.a` (*archive*, архив).

Одним из преимуществ статических библиотек является то, что вам нужно распространять только исполняемый файл, чтобы пользователи могли запускать вашу программу. Поскольку библиотека становится частью вашей программы, это гарантирует, что с вашей программой всегда будет использоваться правильная версия библиотеки. Кроме того, поскольку статические библиотеки становятся частью вашей программы, вы можете использовать их так же, как функции, которые вы написали для своей программы. С другой стороны, поскольку копия библиотеки становится частью каждого исполняемого файла, который ее использует, это может привести к потере большого количества места. Статические библиотеки также не могут быть легко обновлены – для обновления библиотеки необходимо заменить весь исполняемый файл.

### Динамическая библиотека

**Динамическая библиотека** (также называемая *shared library*, "общая библиотека") состоит из подпрограмм, которые загружаются в ваше приложение во время выполнения. Когда вы компилируете программу, использующую динамическую библиотеку, библиотека не становится частью вашего исполняемого файла – она ​​остается отдельной единицей. В Windows динамические библиотеки обычно имеют расширение `.dll` (*dynamic link library*, библиотека динамической компоновки), а в Linux – расширение `.so` (*shared object*, общий объект).

Одним из преимуществ динамических библиотек является то, что многие программы могут совместно использовать одну копию библиотеки, что экономит место. Возможно, большим преимуществом является то, что динамическую библиотеку можно обновить до более новой версии без замены всех исполняемых файлов, которые ее используют.

Поскольку динамические библиотеки не связаны с вашей программой, программы, использующие динамические библиотеки, должны явно загружать и взаимодействовать с динамической библиотекой. Этот механизм может сбивать с толку и затруднять взаимодействие с динамической библиотекой. Чтобы упростить использование динамических библиотек, можно использовать библиотеку импорта.

### Библиотека импорта

**Библиотека импорта** – это библиотека, которая автоматизирует процесс загрузки и использования динамической библиотеки. В Windows это обычно делается с помощью небольшой статической библиотеки (`.lib`) с тем же именем, что и динамическая библиотека (`.dll`). Статическая библиотека подключается к программе во время компиляции, и затем функциональные возможности динамической библиотеки можно эффективно использовать, как если бы это была статическая библиотека. В Linux файл общих объектов (`.so`) выполняет функции динамической библиотеки и библиотеки импорта. Большинство компоновщиков при создании динамической библиотеки могут создать библиотеку импорта для этой динамической библиотеки.

## И как это применить к нашему проекту?

При выполнении лабораторных работ от вас будут требовать декомпозировать ваш код на связанные логические модули и представлять их виде библиотек. Эти библиотеки необходимо будет располагать в директории лабораторной работы, задание которой вы выполняете. Поэтому для наших целей будет подходить создание статической библиотеки.

Давайте на примере обычной библиотеки, умеющей только складывать несколько чисел, рассмотрим как делать статические библиотеки для выполнения лабораторных работ.

## Создание проекта статической библиотеки

В директории создадим поддиректорию `mymath`.

В ней создадим файлы `CMakeLists.txt`, `mymath.cpp` и `mymath.h`. В итоге наша директория с лабораторной работой будет выглядеть следующим образом:

```bash
lab1
├── mymath
│   ├── CMakeLists.txt
│   ├── mymath.cpp
│   └── mymath.h
├── tests
├── CMakeList.txt
├── README.md
└── main.cpp
```

### Содержимое `mymath.cpp`

Оставляем код без комментариев:

```cpp
#include "mymath.h"

namespace mymath {
    int sum(int a, int b) {
        return a + b;
    }
}
```

### Содержимое `mymath.h`

Про заголовочные файлы вроде из предыдущей дисциплины вам должно быть известно?). Поэтому код тоже без комменатриев:

```cpp
#ifndef MYMATH_H
#define MYMATH_H

namespace mymath {
    int sum(int a, int b);
}

#endif //MYMATH_H
```

:::info
На всякий случай `#ifndef` - это про [include guard](https://en.wikipedia.org/wiki/Include_guard):

> Как только заголовок включен, он проверяет, определено ли уникальное значение. Затем, если он не определен, он определяет его и переходит к остальной части кода.
>
> Когда код включается снова, первый `#ifndef` завершается ошибкой, в результате чего получается пустой файл.
>
> Это предотвращает двойное объявление любых идентификаторов, таких как типы, перечисления и статические переменные.

Благодарим за такую познавательную информацию автора ответа на [Stack Overflow](https://stackoverflow.com/questions/1653958/why-are-ifndef-and-define-used-in-c-header-files).
:::

### И самое интересное, `CMakeLists.txt`

:::info
При подготовке материала использовалась статья [Полное руководство по CMake. Часть вторая: Система сборки](https://itnan.ru/post.php?c=1&p=432096).
:::

Вот он, виновник торжества:

```cmake
cmake_minimum_required(VERSION 3.23)

set(project "mymath")
project(${project})

set(CMAKE_CXX_STANDARD 17)

set(${project}_SOURCES
        mymath.cpp)

set(${project}_HEADERS
        mymath.h)

set(${project}_SOURCE_LIST
        ${${project}_SOURCES}
        ${${project}_HEADERS})

add_library(${project}
        STATIC
        ${${project}_SOURCE_LIST})

```

Тут явно решили выделить название проекта в отдельную переменную окружения CMake с помощью вызова функции `set(project "mymath")`.

Переменные можно определить путём вызова команды [set](https://cmake.org/cmake/help/latest/command/set.html), а удалить вызовом [unset](https://cmake.org/cmake/help/latest/command/unset.html). Получить значение переменной можно по конструкции `${VARIABLE}`. Если переменная ещё не определена и где-то потребовалось получить её значение, то данная переменная обратится в пустую строку.

:::info
Мы все еще не упоминали, что CMake имеет относительно простой интерпретируемый императивный язык сценариев, поддерживающий переменные, методы обработки строк, массивы, объявления функций и макросов? Нет?! Ну вот теперь живите с этой информацией.
:::

Далее мы устанавливаем переменные окружения `${project}_HEADERS`, `${project}_SOURCES` и `${project}_SOURCE_LIST`. Тут явное перечисление то, какие исходные файлы и заголовочные файлы мы подключаем. Можно [поиск этих файлов черех `GLOB`](https://stackoverflow.com/questions/17653738/recursive-cmake-search-for-header-and-source-files), но это считается антипаттерном. [В этом выступлении](https://youtu.be/ckO98bRzL9Y?t=1904) объясняется почему. Вкратце: если мы добавляем новый файл, то нам явно нужно переписать `CMakeLists.txt`, а он в свою очередь начнет перегенировать свой проект.

Команда [`add_library`](https://cmake.org/cmake/help/latest/command/add_library.html) компилирует библиотеку с указанным видом и именем из исходников. Важно отметить, что окончательное имя библиотеки зависит от целевой платформы (например, `lib<LibraryName>.a` или `<LibraryName>.lib`).

О том, зачем указывать в `add_library` и `add_executable` в заголовочных файлах в CMake-файлах, пояснили [тут](https://stackoverflow.com/questions/36174499/why-add-header-files-into-add-library-add-executable-command-in-cmake).

Ну вот и все! Остается эту библиотеку подключить к нашему консольному приложению.
